<?php
namespace App\Services\Payment\Order;

use App\Jobs\OrderCallbackJob;
use App\Models\CacheDb\Order\RealTimePayCache;
use App\Models\ConstCode;
use App\Models\ConstCodeArray;
use App\Models\MysqlDb\CompanyModel;
use App\Models\MysqlDb\PaymentOrderModel;
use App\Models\MysqlDb\PaymentAlipayRefundModel;
use App\Models\MysqlDb\PaymentWechatRefundModel;
use App\Models\StatusCode;
use App\Services\CommonReturn;
use App\Services\CommonServices;
use App\Services\Payment\ConfigService;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Yansongda\Pay\Pay;

class OrderServices extends PayServices
{

    /**
     * 创建订单
     * @Author: TinyMeng <666@majiameng.com>
     * @return array
     */
    public function create($params){
        //验证支付类型
        $this->checkPaymentMethod($params['payment_method']);
        $params['params'] = json_encode($params);

        //获取公司信息
        $company = CompanyModel::getByAppid($params['app_id']);
        if(empty($company)) {
            CommonReturn::fail(StatusCode::COMMON_NO_DATA_EXIST,'app_id not found');
        }
        $this->app_id = $params['app_id'];
        $currency = "CNY";
        if($params['payment_method'] == ConstCode::PAYMENT_PAYPAL){
            $currency = "USD";
        }
        $params['currency'] = $currency;
        $params['cid'] = $company['id']; //公司id

        //判断订单是否存在
        $order = $this->model->getOrderByOutTradeNo($params['app_id'],$params['out_trade_no']);
        if(!empty($order)) {
            $params['id'] = $order['id'];
            if($order['is_pay'] == ConstCode::PAY_STATUS_YES){
                CommonReturn::fail(StatusCode::COMMON_MSG,"订单号已支付");
            }
        }
        $id = $this->model->edit($params);

        /** 创建第三方订单 */
        $result = $this->createThirdPayOrder($company,$id);
        return $result;
    }

    /**
     * 获取订单支付状态
     * @param $params
     * @return array|void
     * @throws \Exception
     */
    public function getPayStatus($params){
        $out_trade_no = $params['out_trade_no'];
        //获取订单支付状态
        $real_time = RealTimePayCache::getStatus($out_trade_no);
//        if(1){
        if(!empty($real_time['status']) && $real_time['status'] == ConstCode::REAL_TIME_PAY_USER_PAYING){
            //支付信息
            //第三方支付信息
            $where = [
                ['out_trade_no','=',$out_trade_no],
                ['status','=',ConstCode::STATUS_NORMAL],
            ];
            $order = PaymentOrderModel::where($where)
                ->orderBy('create_time','desc')
                ->orderBy('id','desc')
                ->first();
            if(empty($order)){
                CommonReturn::fail(StatusCode::COMMON_NO_DATA_EXIST,'订单不存在');
            }
            $app_id = $order['app_id'];
            //获取公司信息
            $company = CompanyModel::getByAppid($params['app_id']);
            if(empty($company)) {
                CommonReturn::fail(StatusCode::COMMON_NO_DATA_EXIST,'app_id not found');
            }

            //获取第三方支付配置
            $payment = ConstCodeArray::getPayMent($order['payment_method']);
            $config = (new ConfigService)->getParamsConfig($company,$payment);
            //第三方信息,查询订单支付状态
            if($order['payment_method'] == ConstCode::PAYMENT_WECHAT){

                //微信
                $data = [
                    'out_trade_no' => $order['out_trade_no'],
                ];
                $service = Pay::$payment($config);
                $orderDetail = $service->query($data)->toArray();
                if($orderDetail['trade_state'] == 'PAYERROR'){
                    /**
                     * 1.支付超时45s/失败
                     * 2.撤销订单
                     */
                    $cancelResult = Pay::$payment($config)->close($order);
                    if($cancelResult['return_code'] != 'SUCCESS'){
                        CommonReturn::fail(StatusCode::COMMON_NO_DATA_EXIST,$cancelResult['trade_state_desc']);
                    }
                    $status = ConstCode::REAL_TIME_PAY_FAIL;
                    RealTimePayCache::setStatus($out_trade_no,$status);
                    $real_time['status'] = $status;
                }elseif($orderDetail['trade_state'] == 'SUCCESS'){
                    /**
                     * 支付成功
                     */
                    //修改支付状态
                    $total_fee = bcdiv($orderDetail['amount']['total'], 100,2);
                    $success_time = CommonServices::dateTimeSetZone($orderDetail['success_time'])->getTimestamp();

                    $pay = $this->isPay($app_id,$orderDetail['out_trade_no'],$total_fee,$success_time,$orderDetail['transaction_id']);
                    if($pay == false){
                        Log::info('Wechat 订单:'.$orderDetail['out_trade_no'].'pos 订单不存在');//记录日志
                        $real_time_status = ConstCode::REAL_TIME_PAY_FAIL;
                    }else{
                        Log::info('Wechat 订单:'.$orderDetail['out_trade_no'].'pos 状态支付成功');//记录日志
                        $real_time_status = ConstCode::REAL_TIME_PAY_SUCCESS;

                        //回调订单状态
                        OrderCallbackJob::dispatch($pay);
                    }

                    RealTimePayCache::setStatus($out_trade_no,$real_time_status);
                    $real_time['status'] = $real_time_status;
                }else{
                    CommonReturn::fail(StatusCode::COMMON_NO_DATA_EXIST,$orderDetail['trade_state_desc']);
                }
            }elseif($order['payment_method'] == ConstCode::PAYMENT_ALIPAY){
                //支付宝
                $data = [
                    'out_trade_no' => $order['out_trade_no'],
                ];
                $service = Pay::$payment($config);
                $orderDetail = $service->query($data)->toArray();
                if($orderDetail['trade_status'] == 'TRADE_CLOSED'){
                    /**
                     * 1.支付超时/失败
                     * 交易关闭时间：当面付默认3小时。 手机网站支付、电脑网站支付、app支付默认15天。
                     * 2.撤销订单
                     */
                    $real_time_status = ConstCode::REAL_TIME_PAY_FAIL;
                    RealTimePayCache::setStatus($out_trade_no,$real_time_status);
                    $real_time['status'] = $real_time_status;
                }elseif($orderDetail['trade_status'] == 'TRADE_SUCCESS'){
                    /**
                     * 支付成功
                     */
                    //修改支付状态
                    $total_fee = $orderDetail['total_amount'];
                    $pay = $this->isPay($app_id,$orderDetail['out_trade_no'],$total_fee,NOW_TIME,$orderDetail['trade_no']);
                    if($pay == false){
                        Log::info('Alipay 订单:'.$orderDetail['out_trade_no'].'pos 订单不存在');//记录日志
                        $real_time_status = ConstCode::REAL_TIME_PAY_FAIL;
                    }else{
                        Log::info('Alipay 订单:'.$orderDetail['out_trade_no'].'pos 状态支付成功');//记录日志
                        $real_time_status = ConstCode::REAL_TIME_PAY_SUCCESS;

                        //回调订单状态
                        OrderCallbackJob::dispatch($pay);
                    }

                    RealTimePayCache::setStatus($out_trade_no,$real_time_status);
                    $real_time['status'] = $real_time_status;
                }
            }
        }

        $result = [
            'real_time_status'=>$real_time['status']
        ];
        return $result;
    }

    /**
     * 退款
     * @param $params
     * @return array
     */
    public function refund($params){
        if($params['refund_amount'] <= 0){ //退款金额零元
                CommonReturn::fail(StatusCode::COMMON_MSG,'退款金额不能小于0！');
        }
        //订单线上退款加锁（5s以内不可重复）
        $lock = Cache::lock("refund".$params['out_trade_no'],5);
        if(!$lock->get()){
            CommonReturn::fail(StatusCode::COMMON_REPEATED_SUBMISSION);
        }

        //查询订单
        $app_id = $params['app_id'];
        //获取公司信息
        $company = CompanyModel::getByAppid($params['app_id']);
        if(empty($company)) {
            CommonReturn::fail(StatusCode::COMMON_NO_DATA_EXIST,'app_id not found');
        }

        $out_trade_no = $params['out_trade_no'];
        $order = $this->model->getOrderByOutTradeNo($app_id,$out_trade_no);
        if(empty($order)){
            CommonReturn::fail(StatusCode::COMMON_MSG,'第三方支付信息不存在，请联系管理员！');
        }
        if($order['is_pay'] != ConstCode::PAY_STATUS_YES){
            CommonReturn::fail(StatusCode::COMMON_MSG,'订单未支付不能发起退款！');
        }
        $allow_refund_amount = bcsub($order['amount'],$order['refund_amount'],2);
        if($allow_refund_amount < $params['refund_amount']){
            CommonReturn::fail(StatusCode::COMMON_MSG,'当前订单退款金额不能大于'.$allow_refund_amount.'！');
        }

        //退款单号
        $out_refund_no = 'T'.date('YmdHis').rand(10000,99999);

        //获取第三方支付配置
        $payment = ConstCodeArray::getPayMent($order['payment_method']);
        $config = (new ConfigService)->getParamsConfig($company,$payment);
        if($order['payment_method'] == ConstCode::PAYMENT_ALIPAY){
            try{
                $data = [
                    'out_trade_no' => $order['out_trade_no'],
                    'out_request_no' => $out_refund_no,
                    'refund_amount' => $params['refund_amount'],
                    'refund_reason' => $params['refund_reason']??'',
                ];
                $result = Pay::$payment($config)->refund($data)->toArray();
                if($result['code'] == 10000){
                    //记录退款日志
                    PaymentAlipayRefundModel::add($result,$company['id']);
                    //记录多次退款金额
                    $order->refund_amount += $params['refund_amount'];
                    $order->save();
                }else{
                    $result['message'] = $result['msg'].($result['sub_msg']??'');
                    CommonReturn::fail(StatusCode::COMMON_MSG,$result['message']);
                }
            }catch (\Exception $exception){
                $this->log('refund '.class_basename($exception),$exception);
                CommonReturn::fail(StatusCode::COMMON_UNKNOWN,$exception->getMessage());
            }
        }elseif($order['payment_method'] == ConstCode::PAYMENT_WECHAT){
            //退款单号
            $data = [
                'out_trade_no' => $order['out_trade_no'],
                'out_refund_no' => $out_refund_no,
                'amount' => [
                    'refund' => $params['refund_amount']*100,
                    'total' => $order['amount']*100,
                    'currency' => $order['currency'],
                ],
            ];
            $result = Pay::$payment($config)->refund($data)->toArray();
            if(!empty($result['code'])){
                if($result['code'] == 'NOT_ENOUGH'){
                    //基本账户余额不足，请充值后重新发起
                    $result['message'] = '基本账户余额不足，请充值后重新发起';
                }
                CommonReturn::fail(StatusCode::COMMON_MSG,$result['message']);
            }else{
                //记录退款日志
                $result['amount_num'] = $params['refund_amount'];//退款金额
                PaymentWechatRefundModel::add($result,$company['id']);
                //记录多次退款金额
                $order->refund_amount += $params['refund_amount'];
                $order->save();
            }
        }else{
            CommonReturn::fail(StatusCode::COMMON_MSG,"线上退款暂不支持该支付类型的退款");
        }
        return $order;
    }

    /**
     * 关闭订单
     * @param $params
     * @return array|void
     * @throws \Yansongda\Pay\Exception\ContainerException
     * @throws \Yansongda\Pay\Exception\InvalidParamsException
     * @throws \Yansongda\Pay\Exception\ServiceNotFoundException
     */
    public function close($params)
    {
        //查询订单
        $app_id = $params['app_id'];
        $out_trade_no = $params['out_trade_no'];
//        $pay = $this->model->getOrderByOutTradeNo($app_id,$out_trade_no);
//        if(empty($pay)){
//            CommonReturn::fail(StatusCode::COMMON_MSG,'第三方支付信息不存在，请联系管理员！');
//        }
//        if($pay['is_pay'] == ConstCode::PAY_STATUS_YES){
//            CommonReturn::fail(StatusCode::COMMON_MSG,'订单已支付不能发起关闭！');
//        }

        //获取第三方支付配置
//        $payment = ConstCode::getPayMent($pay['payment_method']);
//        $config = ConfigService::getParamsConfig($app_id,$payment);

        $message_list = [];
        if (empty($pay)) {
            return false;
        }

        $where = [
            'pay_status' => ConstCode::PAY_STATUS_NO,
            'pay_id' => $pay['id'],
        ];
        $list = PaymentOrderModel::where($where)->get();
        if (!empty($list)) {
            try {
                foreach ($list as $value) {
                    if ($value['payment'] == ConstCode::PAYMENT_ALIPAY) {
                        $order = [
                            'out_trade_no' => $value['out_trade_no'],
                        ];
                        \Yansongda\LaravelPay\Facades\Pay::alipay()->close($order);
                    } elseif ($value['payment'] == ConstCode::PAYMENT_WECHAT) {
                        $order = [
                            'out_trade_no' => $value['out_trade_no'],
                        ];
                        $result = Pay::wechat()->close($order);
                        $result = $result->toArray();
                        if ($result['return_code'] != 'SUCCESS') {
                            $message_list[] = $value['out_trade_no'] . ':' . $result['return_msg'];
                        }
                    }
                }
            } catch (\Yansongda\Pay\Exceptions\GatewayException $exception) {
                //第三方抛出异常（支付宝扫码支付）
                $message = $exception->getMessage();
                $message = substr($message, strrpos($message, ":") + 1);
                if (strstr($message, 'ACQ.TRADE_NOT_EXIST') !== false) {
//                    $message_list[] = $value['out_trade_no'].':'."交易不存在";
                } else {
                    $message_list[] = $value['out_trade_no'] . ':' . $exception->getMessage();
                }
            } catch (\Exception $exception) {
                $message_list[] = $value['out_trade_no'] . ':' . $exception->getMessage();
            }
        }
        return implode(',', $message_list);
    }

}
